#include "downloader.h"

#include <QUrl>
#include <QFileInfo>
#include <QTextCodec>
#include <QDir>
#include <QDebug>


namespace FileTransfer {

DownLoader::DownLoader(QObject *parent) : QObject(parent)
{
    m_manager = new QNetworkAccessManager(this);
    m_file = 0;
    m_buffer = 0;
    m_extracter = 0;
    m_reply = 0;
    m_overwriteFiles = false;
    m_isCache = false;
    m_isBuffer = false;
    m_tempExtension = ".part";
}

DownLoader::~DownLoader()
{
}


void DownLoader::onHeadersChanged()
{
    if (m_reply->error() > 0) {
        m_errorString = tr("Network error: %1").arg(QString::number(m_reply->error()));
        return;
    }

    int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    if ((statusCode >= 300) && (statusCode <= 399)) {
        m_redirectLocation = m_reply->rawHeader(QString("location").toLatin1());
        m_reply->abort();
        return;
    }

    if (m_reply->hasRawHeader(QString("Content-Disposition").toLatin1())) {
        QString inf = m_reply->rawHeader(QString("Content-Disposition").toLatin1());
        int startPos = inf.indexOf("filename=", 0, Qt::CaseInsensitive);
        if (startPos >= 0) {
            int len = inf.length() - startPos - QString("filename=").length();
            if (len > 0) {
                m_fileName = inf.right(len);
                m_fileName.remove(QChar('\"'));
            }
        }
    }

    qint64 size = m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong();

    // Oper buffer for writting
    if (m_isBuffer) {
        m_buffer = new QBuffer;
        m_buffer->open(QIODevice::ReadWrite);
    }

    if (m_isCache == false)
        return;

    if (!m_overwriteFiles) {
        while (QFile::exists(m_path + m_fileName)) {
            m_fileName.append("_1");
        }
    }

    if (!QDir(m_path).exists()) {
        QDir().mkpath(m_path);
    }

    // Open file for writting
    emit started(m_fileName, size, m_fileUrl);
    m_file = new QFile(m_path + m_fileName + m_tempExtension);
    if (!m_file->open(QIODevice::ReadWrite)) {
        m_errorString = tr("Can't create file %1").arg(m_fileName);
        m_reply->abort();
        return;
    }
}


void DownLoader::onDataReady()
{
    if (m_reply->error() > 0) {
        m_errorString = tr("Network error: %1").arg(QString::number(m_reply->error()));
        return;
    }

    QByteArray bytes = m_reply->readAll();

    if (m_isBuffer)
        m_buffer->write(bytes);

    if (m_isCache == false)
        return;

    if (m_file->write(bytes) < 0) {
        m_errorString = tr("Error writing file %1").arg(m_fileName);
        m_reply->abort();
        return;
    }
}


void DownLoader::onRequestFinished()
{
    if (!m_redirectLocation.isEmpty()) {
        download(QString(m_redirectLocation), m_overwriteFiles, m_folder);
        return;
    }

    if (m_reply->error() > 0 && (m_errorString.isEmpty())) {
        m_errorString = tr("Network Error: %1").arg(QString::number(m_reply->error()));
    }

    if (m_isCache == false) {
        emit finished(m_errorString, m_isAborted, m_isPaused);
        return;
    }

    if (m_file) {
        m_file->close();
        if ((m_isAborted || (!m_errorString.isEmpty())) && (!m_isPaused)) {
            m_file->remove();
        } else {
            QString fileName = m_file->fileName();
            fileName = fileName.mid(0, fileName.size() - m_tempExtension.size());
            m_file->rename(fileName);
        }
        delete m_file;
    }
    m_file = 0;

    emit finished(m_errorString, m_isAborted, m_isPaused);
}


void DownLoader::onDataCodecUrlReceived(const QString &urlString)
{
    if (urlString.isEmpty()) {
        emit finished(tr("Error: Can't receive file URL"), m_isAborted, m_isPaused);
        return;
    }
    download(urlString, m_overwriteFiles, m_folder);
}


void DownLoader::download(const QString &urlString,
                          bool overwrite,
                          const QString &folder)
{
    m_isAborted = false;
    m_isPaused = false;
    m_errorString.clear();
    m_redirectLocation.clear();

    QUrl url;
    m_fileUrl = urlString;
    url.setUrl(urlString.toLatin1());
    if (!url.isValid()) {
        emit finished(tr("Error: Bad file URL"), m_isAborted, m_isPaused);
        return;
    }

    if (!folder.isEmpty()) {
        m_isBuffer = false;
        m_isCache = true;
        m_overwriteFiles = overwrite;

        // fileName from url
        QFileInfo fileInfo(url.path());
        m_fileName = fileInfo.fileName();
        if (m_fileName.isEmpty()) {
            m_fileName = "index.html";
        }

        if (folder.right(1) != "/") {
            m_path = folder;
            m_path.append("/");
        } else {
            m_path = folder;
        }
    } else {
        m_isBuffer = true;
        m_isCache = false;
        m_fileName = "index.html";
        m_path = QDir::currentPath() + "MeTalkTemp";
    }

    QNetworkRequest request(url);
    request.setRawHeader("User-Agent", "Wget");
    request.setRawHeader("Accept", "*/*");
    request.setRawHeader("Connection", "Keep-Alive");

    m_reply = m_manager->get(request);

    connect(m_reply, SIGNAL(metaDataChanged()),
            this, SLOT(onHeadersChanged()));

    connect(m_reply, SIGNAL(readyRead()),
            this, SLOT(onDataReady()));

    connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
            this, SIGNAL(progress(qint64, qint64)));

    connect(m_reply, SIGNAL(finished()),
            this, SLOT(onRequestFinished()));
}


void DownLoader::downloadFromDataCodec(const QString &urlString,
                                       const QString &folder,
                                       bool overwrite)
{
    m_isAborted = false;
    m_overwriteFiles = overwrite;
    if (!m_extracter) {
        m_extracter = new UrlExtracter(this);
        connect(m_extracter, SIGNAL(done(QString)), this, SLOT(onDataCodecUrlReceived(QString)));
    }
    m_folder = folder;
    m_extracter->getUrl(urlString);
}


void DownLoader::cancelDownload()
{
    m_isAborted = true;
    m_errorString = tr("Error: Aborted by User");
    if (m_extracter)
        m_extracter->abort();
    if (m_reply)
        m_reply->abort();
}


void DownLoader::pauseDownload()
{
    m_isPaused = true;
    m_errorString = tr("Paused");
    if (m_extracter)
        m_extracter->abort();
    if (m_reply)
        m_reply->abort();
}

} // namespace FileTransfer
